package service

import (
	"encoding/xml"
	"fmt"
	"gin-epg/internal/app/dao"
	"gin-epg/internal/app/entity"
	"gin-epg/internal/app/model"
	"gin-epg/internal/app/store"
	"sort"
	"strconv"
	"time"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

// 构建 XML 数据，支持传入 beforeDay 和 afterDay 参数
func buildXml(db *gorm.DB, beforeDay, afterDay int) (*model.TV, error) {
	doc := &model.TV{
		InfoName: "by xmlbuilder2",
		InfoURL:  "http://epg.example.com",
	}

	// 查询频道表，建立 channelid -> channel name 的 map 映射
	var channels []entity.EpgChannel
	if err := db.Find(&channels).Error; err != nil {
		dao.Logger.Errorf("查询频道表时出错: %v", err)
		return nil, err
	}
	channelMap := make(map[string]string)
	for _, channel := range channels {
		channelMap[strconv.Itoa(channel.Id)] = channel.Name
	}

	// 建立频道反向 map
	channelMapReverse := make(map[string]string)
	for k, v := range channelMap {
		channelMapReverse[v] = k
	}

	// 查询指定时间范围内的节目表
	var programs []entity.EpgProgram
	if err := db.Where("date BETWEEN ? AND ?", time.Now().AddDate(0, 0, -beforeDay).Format("2006-01-02"), time.Now().AddDate(0, 0, afterDay).Format("2006-01-02")).
		Order("channel ASC, date ASC, start ASC").
		Find(&programs).Error; err != nil {
		dao.Logger.Errorf("查询节目表时出错: %v", err)
		return nil, err
	}

	// 将 programs 按 channel 分组，建立 channel -> programs 列表分组，并且 channel 内的列表按 date, start 字段升序
	programsMap := make(map[string][]entity.EpgProgram)
	for _, program := range programs {
		programsMap[program.Channel] = append(programsMap[program.Channel], program)
	}

	// map 内的 value 要实现排序，先按 date 排序，date 相同时按 start 排序
	for _, programs := range programsMap {
		sort.Slice(programs, func(i, j int) bool {
			if programs[i].Date.Time.Time == programs[j].Date.Time.Time {
				return programs[i].Start < programs[j].Start
			}
			return programs[i].Date.Time.Time.Before(programs[j].Date.Time.Time)
		})
	}

	// 处理需要生成的别名 id 格式 aliaid_channelid，programsMap 也需要增加对应的数据
	// 查询 epg_channel_alias 表 is_export_xmltv=1 的数据
	var channelAliases []entity.EpgChannelAlias
	if err := db.Where("is_export_xmltv = ?", "1").Find(&channelAliases).Error; err != nil {
		dao.Logger.Errorf("查询别名表时出错: %v", err)
		return nil, err
	}
	for _, alias := range channelAliases {
		channelId := strconv.Itoa(alias.Id) + "_" + channelMapReverse[alias.Name]
		channelAliasName := alias.Alias
		if programs, ok := programsMap[alias.Name]; ok {
			programsMap[channelAliasName] = programs
			channelMap[channelId] = channelAliasName
		}
	}

	// 遍历 channelMap
	for key, value := range channelMap {
		channel := model.Channel{ID: key, DisplayName: value}
		doc.Channels = append(doc.Channels, channel)

		if programs, ok := programsMap[value]; ok {
			for _, program := range programs {
				startTime, _ := time.Parse("2006-01-02 15:04", program.Date.Time.Time.Format("2006-01-02")+" "+program.Start)
				endTime, _ := time.Parse("2006-01-02 15:04", program.Date.Time.Time.Format("2006-01-02")+" "+program.End)
				if endTime.Before(startTime) {
					endTime = endTime.AddDate(0, 0, 1) // 如果 end 时间早于 start 时间，则将 end 时间增加一天
				}
				start := startTime.Format("20060102150400") + " +0800"
				stop := endTime.Format("20060102150400") + " +0800"

				programme := model.Programme{
					Channel: key,
					Start:   start,
					Stop:    stop,
					Title:   program.Title,
				}
				doc.Programmes = append(doc.Programmes, programme)
			}
		}
	}

	return doc, nil
}

// 处理 9 天 XML 生成的定时任务
func HandleEpgXmltv9dayGenCron() error {
	// 调用 buildXml 函数
	tv, err := buildXml(dao.SqlSession, 6, 2)
	if err != nil {
		dao.Logger.Errorf("failed to build 9 day XML: %v", err)
		return fmt.Errorf("failed to build 9 day XML: %v", err)
	}

	// 将 XML 输出到字符串
	xmlData, err := xml.MarshalIndent(tv, "", "  ")
	if err != nil {
		dao.Logger.Errorf("failed to marshal XML: %v", err)
		return fmt.Errorf("failed to marshal XML: %v", err)
	}

	storage := store.GlobalStoreService

	// 使用存储实例保存数据
	if err := storage.SaveString("e9.xml", string(xmlData)); err != nil {
		dao.Logger.Errorf("failed to save data: %v", err)
		return fmt.Errorf("failed to save data: %v", err)
	} else {
		dao.Logger.Info("Data saved successfully.")
	}

	// 调用 storage.saveKeyGz 压缩
	if err := storage.SaveKeyGz("e9.xml"); err != nil {
		dao.Logger.Errorf("failed to save compressed data: %v", err)
		return fmt.Errorf("failed to save compressed data: %v", err)
	} else {
		dao.Logger.Info("Data gz saved successfully.")
	}

	return nil
}

// 处理 7 天 XML 生成的定时任务
func HandleEpgXmltv7dayGenCron() error {
	// 调用 buildXml 函数
	tv, err := buildXml(dao.SqlSession, 6, 0)
	if err != nil {
		dao.Logger.Errorf("failed to build 7 day XML: %v", err)
		return fmt.Errorf("failed to build 7 day XML: %v", err)
	}

	// 将 XML 输出到字符串
	xmlData, err := xml.MarshalIndent(tv, "", "  ")
	if err != nil {
		dao.Logger.Errorf("failed to marshal XML: %v", err)
		return fmt.Errorf("failed to marshal XML: %v", err)
	}

	storage := store.GlobalStoreService

	// 使用存储实例保存数据
	if err := storage.SaveString("e7.xml", string(xmlData)); err != nil {
		dao.Logger.Errorf("failed to save data: %v", err)
		return fmt.Errorf("failed to save data: %v", err)
	} else {
		dao.Logger.Info("Data saved successfully.")
	}

	// 调用 storage.saveKeyGz 压缩
	if err := storage.SaveKeyGz("e7.xml"); err != nil {
		dao.Logger.Errorf("failed to save compressed data: %v", err)
		return fmt.Errorf("failed to save compressed data: %v", err)
	} else {
		dao.Logger.Info("Data gz saved successfully.")
	}

	return nil
}

// 处理 1 天 XML 生成的定时任务
func HandleEpgXmltv1dayGenCron() error {
	// 调用 buildXml 函数
	tv, err := buildXml(dao.SqlSession, 0, 0)
	if err != nil {
		dao.Logger.Errorf("failed to build 1 day XML: %v", err)
		return fmt.Errorf("failed to build 1 day XML: %v", err)
	}

	// 将 XML 输出到字符串
	xmlData, err := xml.MarshalIndent(tv, "", "  ")
	if err != nil {
		dao.Logger.Errorf("failed to marshal XML: %v", err)
		return fmt.Errorf("failed to marshal XML: %v", err)
	}

	storage := store.GlobalStoreService

	// 使用存储实例保存数据
	if err := storage.SaveString("e.xml", string(xmlData)); err != nil {
		dao.Logger.Errorf("failed to save data: %v", err)
		return fmt.Errorf("failed to save data: %v", err)
	} else {
		dao.Logger.Info("Data saved successfully.")
	}

	// 调用 storage.saveKeyGz 压缩
	if err := storage.SaveKeyGz("e.xml"); err != nil {
		dao.Logger.Errorf("failed to save compressed data: %v", err)
		return fmt.Errorf("failed to save compressed data: %v", err)
	} else {
		dao.Logger.Info("Data gz saved successfully.")
	}

	return nil
}
